<?php
namespace VpnTest;

use Cache\Redis;
use Db\MySQL\SQLFacade;
use HttpRequester\CurlFacade as Curl;

class tester
{
    public $valid_express_nos;
    protected $vpns = [
        // '10.117.192.194:4433',
        '10.47.60.139:4433',
        // '10.51.43.253:4433',
    ];
    protected $url_tpls = [
        'http://express.interface.kuaidihelp.com/infor.php?company=%s&express_no=%s&type=express.get&no_cache=1',
    ];

    protected $url_tpl = 'http://express.interface.kuaidihelp.com/infor.php?company=%s&express_no=%s&type=express.get&no_cache=1';

    private static $db;

    private $log_file;

    public function __construct($log_file_name = null)
    {
        $this->log_file = $log_file_name ?: date('Y_m_d_H_i_s') . '.txt';
    }

    public function run($limit = 200, $total_run_limit = 500)
    {
        $waybills = $this->getWaybills(2000);
        shuffle($waybills);
        $waybills = array_slice($waybills, 0, $limit);
        $sources = [
            'kuaidi',
            'kuaidi100',
        ];
        // $val = ['zt','11111111111111111'];
        // $proxy = $this->vpns[mt_rand(0,2)];
        // $url = $this->getUrl($val, ['proxy' => $proxy, 'source' => $sources[mt_rand(0, 1)]]);

        // exit($url);
        $i = 0;
        foreach ($waybills as $key => $val) {
            foreach ($this->vpns as $proxy) {
                $i++;
                $url = $this->getUrl($val, ['proxy' => $proxy, 'source' => $sources[mt_rand(0, 1)]]);
                echo $key, '/' . $total_run_limit . '  ', $proxy, '  ', $url, PHP_EOL;
                echo implode(' ', $val), PHP_EOL;
                $data = Curl::get($url);
                var_dump($data);
                if ($i >= $total_run_limit) {
                    break 2;
                }
            }
        }

        // var_dump($data);
    }

    public function runOnline($limit = 200, $total_run_limit = 500, $specified_vpn = null)
    {
        $waybills = $this->getWaybills(2000);
        shuffle($waybills);
        $waybills = array_slice($waybills, 0, $limit);
        $sources = [
            'kuaidi',
            'kuaidi100',
        ];
        date_default_timezone_set('PRC');
        // $val = ['zt','11111111111111111'];
        // $proxy = $this->vpns[mt_rand(0,2)];
        // $url = $this->getUrl($val, ['proxy' => $proxy, 'source' => $sources[mt_rand(0, 1)]]);

        // exit($url);
        $i = 0;
        $n = 0;
        $error_msg = "notice: %s try to get valid vpn";
        $not_get_error = 'failure get valid vpn while retry three times,will try next round!';
        $failure_times = 0;
        foreach ($waybills as $key => $val) {
            $try_times = 0;
            $start = microtime(true);
            while (!($proxy = $this->getAvaliableVpn($specified_vpn)) && $try_times < 3) {
                $try_times++;
                $n++;
                echo $e_msg = sprintf($error_msg, $try_times), PHP_EOL;
                sleep(5);
                $this->log($e_msg);
            }
            if ($try_times >= 3 && !$proxy) {
                $end = microtime(true);
                $this->log($n . '  ' . $not_get_error . '   cost:' . $start - $end);
                echo $n,'  ', $not_get_error, PHP_EOL;
                $i++;
                continue;
            }
            $i++;
            $source = $sources[mt_rand(0, 1)];
            $url = $this->getUrl($val, ['proxy' => $proxy, 'source' => $source]);
            echo $key, '/' . $total_run_limit . '  ', $proxy, '  ', $url, PHP_EOL;
            echo implode(' ', $val), PHP_EOL;
            $data = Curl::get($url);
            if ($data === 'false') {
                $failure_times++;
                if ($failure_times > 5) {
                    if ($ip = $this->getProxyViaIp($proxy)) {
                        $this->log($failure_times.'  '.$proxy . ' ' . $ip . ' maybe blocked by ' . $source);
                        $failure_times = 0;
                    }
                }
            }
            var_dump($data);
            if ($i >= $total_run_limit) {
                break;
            }

        }

        // var_dump($data);
    }

    public function getProxyViaIp($proxy)
    {
        if ($proxy) {
            $url = 'http://httpbin.org/ip';
            $options = [
                CURLOPT_PROXY => $proxy,
            ];
            if ($data = Curl::get($url, $options)) {
                $data = json_decode($data, true);
                return isset($data['origin']) ? $data['origin'] : '';
            }
        }
        return false;
    }

    public function getAvaliableVpn($specified = null)
    {
        $additional = '';
        if ($specified !== null) {
            $additional = ' AND proxy = "' . $specified . '"';
        }
        $db_config = [
            'host' => '10.168.177.85',
            'username' => 'reader',
            'password' => 'Ut90CVgeOwwwfL%d',
            'db' => 'dts',
            'port' => 3311,
            'prefix' => '',
            'charset' => 'utf8',
        ];
        $sql = 'SELECT proxy FROM proxy_vpn WHERE status = 1' . $additional;
        if ($data = static::getDb($db_config)->rawQuery($sql)) {
            if (($proxies = array_column($data, 'proxy')) && is_array($proxies)) {
                return $proxies[array_rand($proxies)];
            }
        }
        return false;
    }

    public function log($msg = '')
    {
        $msg = date('Y-m-d H:i:s') . "   " . $msg . "   \n";
        file_put_contents($this->log_file, $msg, FILE_APPEND);
    }

    public static function getDb(array $db_config = null)
    {

        $db_config === null and $db_config = [
            'host' => '192.168.1.81',
            'username' => 'dbworker',
            'password' => 'dbwokerpwd',
            'db' => 'express',
            'port' => 3306,
            'prefix' => '',
            'charset' => 'utf8',
        ];
        if (!($key = $db_config['host'])) {
            die('error parameters provided');
        }
        if (!isset(static::$db[$key])) {

            static::$db[$key] = SQLFacade::getInstance($db_config);
        }
        return static::$db[$key];
    }

    public function getUrl(array $data, array $rand_data = null)
    {
        $str = '';
        if (is_array($rand_data)) {
            $str = $this->buildQueryString($rand_data);
        }
        return trim(vsprintf($this->url_tpl, $data), '&') . $str;
    }

    private function buildQueryString(array $data)
    {
        return '&' . http_build_query($data);
    }

    public function getWaybills($limit = 1000)
    {
        $cache_key = 'vpntest:waybillnumbers';
        if (!$data = Redis::get($cache_key)) {
            $sql = "SELECT brand,waybill_no FROM express_valid_waybills WHERE brand in ('zt','sto') ORDER BY id  LIMIT " . (int) $limit;
            if ($data = static::getDb()->rawQuery($sql)) {
                Redis::set($cache_key, serialize($data), 3600);
            }
        }
        return is_array($data) ? $data : unserialize($data);
    }

    public function repeater($func = null, array $params = [], $times = 10)
    {
        $timer['start'] = microtime(true);
        if (is_callable($func)) {
            while ($times > 0) {
                echo $times--, PHP_EOL;
                call_user_func($func, $params);
            }
        }
        $timer['end'] = microtime(true);
        $timer['cost'] = $timer['end'] - $timer['start'];
        echo implode(' ', $timer), PHP_EOL;
    }

}
